'use strict';

function PromiseA(then) {
  this._state = 0;
  this._value = null;
  this._deferreds = [];

  if (then === this.then) return;

  performThen(then, resolve.bind(this), reject.bind(this));
}

function addLockToListener(onFulfilled, onRejected) {
  let done = false;
  function onFulfilledWrapper(value) {
    if (done) return;
    done = true;
    onFulfilled(value);
  }
  function onRejectedWrapper(value) {
    if (done) return;
    done = true;
    onRejected(value);
  }
  return [onFulfilledWrapper, onRejectedWrapper];
}

function performThen(then, onFulfilled, onRejected) {
  const [lockOnFulfilled, lockOnRejected] = addLockToListener(
    onFulfilled,
    onRejected,
  );
  try {
    then(lockOnFulfilled, lockOnRejected);
  } catch (error) {
    lockOnRejected(error);
  }
}

function resolve(value) {
  if (
    value !== null &&
    (typeof value === 'object' || typeof value === 'function')
  ) {
    if (value === this) {
      reject.call(this, new TypeError('...'));
      return;
    }

    if (value instanceof PromiseA) {
      this._state = 3;
      this._value = value;
      flushDeferred(this);
      return;
    }

    let then = null;
    try {
      then = value.then;
    } catch (err) {
      reject.call(this, err);
      return;
    }
    if (typeof then === 'function') {
      performThen(then.bind(value), resolve.bind(this), reject.bind(this));
      return;
    }
  }

  this._value = value;
  this._state = 1;
  flushDeferred(this);
}
function reject(value) {
  this._value = value;
  this._state = 2;

  flushDeferred(this);
}

function flushDeferred(promise) {
  for (let i = 0; i < promise._deferreds.length; i++) {
    const performInnerThenWrapper = promise._deferreds[i];
    handlePerformInnerThen(promise, performInnerThenWrapper);
  }
  promise._deferreds.length = 0;
}

const innerThenWrapper = (originalOnFulfilled, originalOnRejected) =>
  function innerThen(onFulfilled, onRejected) {
    const parentPromise = this;
    const originalCallback =
      parentPromise._state === 1 ? originalOnFulfilled : originalOnRejected;
    // 如果 innerThen 有对应状态的监听器，则值有监听器决定，监听器执行没有异常则状态为完成的
    // 否则，状态和值继承父 promise
    if (typeof originalCallback === 'function') {
      onFulfilled(originalCallback(parentPromise._value));
    } else {
      const callback = parentPromise._state === 1 ? onFulfilled : onRejected;
      callback(parentPromise._value);
    }
  };

PromiseA.prototype.then = function (onFulfilled, onRejected) {
  const parentPromise = this;
  const promise = new PromiseA(this.then);
  const innerThen = innerThenWrapper(onFulfilled, onRejected);

  const performInnerThenWrapper = (parentPromise) => {
    asap(() => {
      performThen(
        innerThen.bind(parentPromise),
        resolve.bind(promise),
        reject.bind(promise),
      );
    });
  };

  handlePerformInnerThen(parentPromise, performInnerThenWrapper);

  return promise;
};

function handlePerformInnerThen(promise, performInnerThenWrapper) {
  while (promise._state === 3) {
    promise = promise._value;
  }
  if (promise._state === 0) {
    promise._deferreds.push(performInnerThenWrapper);
  } else {
    performInnerThenWrapper(promise);
  }
}

function asap(fn) {
  setTimeout(fn, 0);
}

module.exports = PromiseA;
